/*
============================================================================
 Name			: BlueToothServiceAdvertiser.cpp
 Author	  		: 
 Copyright   	:
 Description 	: Implemetation
============================================================================
*/

// INCLUDE FILES
#include "BluetoothServiceAdvertiser.h"
#include "common.hrh"

_LIT(KRpsServiceName, "RPS");
_LIT(KRpsServiceDes, "RPS");

const TInt KPortBufMaxSize = 2;
const TInt KServAvailBufMaxSize = 1;

/*
============================================================================
CBluetoothServiceAdvertiser's constructor
============================================================================
*/
CBluetoothServiceAdvertiser::CBluetoothServiceAdvertiser(MBluetoothServiceAdvertiserObserver& aSerAdvObs)
	:CActive(CActive::EPriorityStandard), iSerAdvObs(aSerAdvObs), iState(EIdle)
	{
	CActiveScheduler::Add(this);
	}

/*
============================================================================
CBluetoothServiceAdvertiser's destructor
============================================================================
*/
CBluetoothServiceAdvertiser::~CBluetoothServiceAdvertiser()
	{
	Cancel();
	UnregisterService();
	}

/*
============================================================================
Entry point of the CBluetoothServiceAdvertiser's state machine. The initial state must be EIdle.
A state other than EIdle will result in a KErrInUse. This is to prevent StartL() calling
more than once if the state machine is alread active.
============================================================================
*/
void CBluetoothServiceAdvertiser::StartL(const TInt aPort)
	{
	if (iState != EIdle)
		{
		User::Leave(KErrInUse);
		}
		
	iPort = aPort;
	SelfComplete();
	}

/*
============================================================================
Connect to the SDP server and open a SDP's  RSdpDatabase subsession where 
service records and their attributes can be added, deleted, and updated.
============================================================================
*/
void CBluetoothServiceAdvertiser::ConnectToSdpL()
	{
	iState = EConnecting;
	User::LeaveIfError(iSdpServ.Connect());
	User::LeaveIfError(iSdpDatabase.Open(iSdpServ));
	SelfComplete();
	}
	
/*
============================================================================
Create and add the RPS's service record into the SDP's database
============================================================================
*/
void CBluetoothServiceAdvertiser::BuildServiceRecordL()
	{
	iState = EBuildingServiceRecord;
	TUUID serviceUUID(KRPS_BTServiceID);
	//Creates a new service record, with a single service class (KRPS_BTServiceID) in the SDP database.
	//If succesful iRecordHandle will hold the RPS's record handle.
	iSdpDatabase.CreateServiceRecordL(serviceUUID, iRecordHandle);

	//Now the RPS's record needs to be filled with RPS's service attributes
	
	// Add the RFCOMM protocol attribute to the record.
	CSdpAttrValueDES* protocolDescriptorList = CSdpAttrValueDES::NewDESL(NULL);
	CleanupStack::PushL(protocolDescriptorList);

    TBuf8<KPortBufMaxSize> port; 
    port.Append((TChar)iPort);
    
    //Builds a protocol list of attributes. In our example the list contains only the RFCOMM protocol element
    //and the RFCOMM's port. For more information about the structure of a SDP's record please refer to the SDK
	protocolDescriptorList
	->StartListL()					//Marks the start of the list
	    ->BuildDESL()				//Adds a Data Element Sequence (DES) 
		->StartListL()				//Indicates that subsequent elements added belong to a DES (Data Element Sequence) or DEA (Data Element Alternative)
			->BuildUUIDL(KRFCOMM)	//Adds the RFCOMM's TUUID
	        ->BuildUintL(port)		//Adds the RFCOMM's port. Remote devices will connect to this port to use the RPS's service
		->EndListL()				//Marks the end of the subsequent element
	->EndListL();					//Marks the end of the list


	//Sets the protocol list to the record
	iSdpDatabase.UpdateAttributeL(iRecordHandle, KSdpAttrIdProtocolDescriptorList, *protocolDescriptorList);
	CleanupStack::PopAndDestroy(protocolDescriptorList);

	//Adds the record's name attribute
	iSdpDatabase.UpdateAttributeL(iRecordHandle, KSdpAttrIdBasePrimaryLanguage + KSdpAttrIdOffsetServiceName, KRpsServiceName);

	//Adds the record's description attribute
	iSdpDatabase.UpdateAttributeL(iRecordHandle, KSdpAttrIdBasePrimaryLanguage + KSdpAttrIdOffsetServiceDescription, KRpsServiceDes);

	//Adds the record's available service attribute
	CSdpAttrValue* attrVal = NULL;
	TBuf8<KServAvailBufMaxSize> avail;
	avail.FillZ(1);
	avail[0]=0xff;//Fully available value
	//Service availability is store as a TUint 
	attrVal = CSdpAttrValueUint::NewUintL(avail);
	CleanupStack::PushL(attrVal);
	iSdpDatabase.UpdateAttributeL(iRecordHandle, KSdpAttrIdServiceAvailability, *attrVal);
	CleanupStack::PopAndDestroy(attrVal);
	SelfComplete();
	}
	
/*
============================================================================
Removes the RPS's service record from the SDP's database
============================================================================
*/
void CBluetoothServiceAdvertiser::UnregisterService()
	{
	// Deletes SDP Record
    if (iRecordHandle)
		{
        TRAPD(ret,iSdpDatabase.DeleteRecordL(iRecordHandle));
		}
    // Closes SDP's database handle
    iSdpDatabase.Close();
    // Close SDP's session
    iSdpServ.Close();
	}
	
/*
============================================================================
Handles the active object's service advertising completion events.
============================================================================
*/
void CBluetoothServiceAdvertiser::RunL()
	{
	switch (iState)
        {
        case EIdle:
        	//State machine entry point
        	ConnectToSdpL();
        	break;
        case EConnecting:
        	//Connect to SDP server completed, start building the RPS's service record
        	BuildServiceRecordL();
            break;  
        case EBuildingServiceRecord:
        	//Build RPS's service record completed, notify the completion event to the observer.
        	iSerAdvObs.AdvertiserComplete();
            break;   
        default:
            User::Leave(KErrCorrupt);
            break;
        };	
	}

/*
============================================================================
DoCancel is called as part of the active object's Cancel().
============================================================================
*/
void CBluetoothServiceAdvertiser::DoCancel()
	{
	//Not Used
	}

/*
============================================================================
Calls User::RequestComplete on this active object. Used to trigger the next step of the state machine.
============================================================================
*/
void CBluetoothServiceAdvertiser::SelfComplete()
	{
	TRequestStatus* status = &iStatus;
	User::RequestComplete(status, KErrNone);
	SetActive();		
	}

/*
============================================================================
Handles a leave occurring in the request completion event handler RunL().
Close all resources, reset the state machine to the EIdle state, notify the
observer of the error
============================================================================
*/
TInt CBluetoothServiceAdvertiser::RunError(TInt aError)
	{
	switch (iState)
        {
        case EConnecting:
            break;
        case EBuildingServiceRecord:
		    // Delete SDP Record
		    if (iRecordHandle)
				{
		        TRAPD(err,iSdpDatabase.DeleteRecordL(iRecordHandle));
				}
		    // Close SDP handles
		    iSdpDatabase.Close();
		    iSdpServ.Close();
            break;   
        default:
        	aError = KErrCorrupt;
            break;
        };
        	
	iState = EIdle;
	iSerAdvObs.ReportAdvertiserErr(aError);
	return KErrNone;
	}
